using System;
using NLog;
using WCell.Constants;
using WCell.Constants.Items;
using WCell.Constants.Looting;
using WCell.Constants.Updates;
using WCell.Core;
using WCell.RealmServer.Database;
using WCell.RealmServer.Handlers;
using WCell.RealmServer.Items;
using WCell.RealmServer.Items.Enchanting;
using WCell.RealmServer.Misc;
using WCell.RealmServer.Modifiers;
using WCell.RealmServer.Quests;
using WCell.Util;
using WCell.Util.NLog;

namespace WCell.RealmServer.Entities
{
	public partial class Item : ObjectBase, IOwned, IWeapon, INamed, ILockable, IQuestHolder, IMountableItem
	{
		private static readonly Logger log = LogManager.GetCurrentClassLogger();

		public static readonly UpdateFieldCollection UpdateFieldInfos = UpdateFieldMgr.Get(ObjectTypeId.Item);

		protected override UpdateFieldCollection _UpdateFieldInfos
		{
			get { return UpdateFieldInfos; }
		}

		public static readonly Item PlaceHolder = new Item();

		protected ItemTemplate m_template;
		protected internal bool m_isInWorld;

		/// <summary>
		/// Items are unknown when a creation update
		/// has not been sent to the Owner yet.
		/// </summary>
		internal bool m_unknown;

		protected Character m_owningCharacter;
		protected BaseInventory m_container;
		protected ItemEnchantment[] m_enchantments;
		protected IProcHandler m_hitProc;

		protected ItemRecord m_record;


		public static Item Create(uint templateId, Character owner, uint amount)
		{
			var template = ItemMgr.GetTemplate(templateId);
			if (template != null)
			{
				return Create(template, owner, amount);
			}
			return null;
		}

		public static Item Create(ItemId templateId, Character owner, uint amount)
		{
			var template = ItemMgr.GetTemplate(templateId);
			if (template != null)
			{
				return Create(template, owner, amount);
			}
			return null;
		}

		public static Item Create(ItemTemplate template, Character owner, uint amount)
		{
			var item = template.Create();
			item.InitItem(template, owner, amount);
			return item;
		}

		public static Item Create(ItemRecord record, Character owner)
		{
			var template = record.Template;
			if (template == null)
			{
				log.Warn("{0} had an ItemRecord with invalid ItemId: {1}", owner, record);
				return null;
			}

			var item = template.Create();
			item.LoadItem(record, owner, template);
			return item;
		}

		public static Item Create(ItemRecord record, Character owner, ItemTemplate template)
		{
			var item = template.Create();
			item.LoadItem(record, owner, template);
			return item;
		}

		public static Item Create(ItemRecord record, ItemTemplate template)
		{
			var item = template.Create();
			item.LoadItem(record, template);
			return item;
		}

		protected internal Item()
		{
		}

		/// <summary>
		/// Creates a new Item
		/// </summary>
		internal void InitItem(ItemTemplate template, Character owner, uint amount)
		{
			m_record = ItemRecord.CreateRecord();

			EntryId = m_record.EntryId = template.Id;

			Type |= ObjectTypes.Item;

			m_template = template;

			Durability = m_template.MaxDurability;
			MaxDurability = m_template.MaxDurability;
			Flags = m_template.Flags;
			TextId = m_template.PageTextId;
			RandomPropertiesId = m_template.RandomPropertiesId != 0 ? m_template.RandomPropertiesId : m_template.RandomSuffixId;
			Amount = amount;
			OwningCharacter = owner;
			EntityId = m_record.EntityId;

			// set charges to max
			if (m_template.UseSpell != null)
			{
				SetSpellCharges(m_template.UseSpell.Index, m_template.UseSpell.Charges);
			}

			if (m_template.RandomPropertyInfo != null)
			{
				// TODO: Add selected random property
				//ApplyRandomProps(m_template.RandomPropertyInfo);
			}

			template.NotifyCreated(m_record);
			OnInit();
		}

		/// <summary>
		/// Loads an already created item
		/// </summary>
		internal void LoadItem(ItemRecord record, Character owner, ItemTemplate template)
		{
			m_record = record;
			EntityId = record.EntityId;

			m_template = template;
			EntryId = m_template.Id;

			Type |= ObjectTypes.Item;

			SetInt32(ItemFields.FLAGS, (int)record.Flags);
			SetInt32(ItemFields.DURABILITY, record.Durability);
			SetInt32(ItemFields.DURATION, record.Duration);
			SetInt32(ItemFields.STACK_COUNT, record.Amount);
			SetInt32(ItemFields.PROPERTY_SEED, record.RandomSuffix);
			SetInt32(ItemFields.RANDOM_PROPERTIES_ID, record.RandomProperty);
			SetEntityId(ItemFields.CREATOR, new EntityId((ulong)record.CreatorEntityId));
			SetEntityId(ItemFields.GIFTCREATOR, new EntityId((ulong)record.GiftCreatorEntityId));

			ItemText = record.ItemText;

			if (m_template.UseSpell != null)
			{
				SetSpellCharges(m_template.UseSpell.Index, (uint)record.Charges);
			}
			MaxDurability = m_template.MaxDurability;

			OwningCharacter = owner;
			OnLoad();
		}

		/// <summary>
		/// Loads an already created item without owner
		/// </summary>
		/// <param name="record"></param>
		/// <param name="template"></param>
		internal void LoadItem(ItemRecord record, ItemTemplate template)
		{
			m_record = record;
			EntityId = record.EntityId;

			m_template = template;
			EntryId = m_template.Id;

			Type |= ObjectTypes.Item;

			SetUInt32(ItemFields.FLAGS, (uint)record.Flags);
			SetInt32(ItemFields.DURABILITY, record.Durability);
			SetInt32(ItemFields.DURATION, record.Duration);
			SetInt32(ItemFields.STACK_COUNT, record.Amount);
			SetInt32(ItemFields.PROPERTY_SEED, record.RandomSuffix);
			SetInt32(ItemFields.RANDOM_PROPERTIES_ID, record.RandomProperty);
			SetInt64(ItemFields.CREATOR, record.CreatorEntityId);
			SetInt64(ItemFields.GIFTCREATOR, record.GiftCreatorEntityId);

			ItemText = record.ItemText;

			if (m_template.UseSpell != null)
			{
				SetSpellCharges(m_template.UseSpell.Index, (uint)record.Charges);
			}
			MaxDurability = m_template.MaxDurability;

			OnLoad();
		}

		/// <summary>
		/// Called after initializing a newly created Item (Owner might be null)
		/// </summary>
		protected virtual void OnInit()
		{
		}

		/// <summary>
		/// Called after loading an Item (Owner might be null)
		/// </summary>
		protected virtual void OnLoad()
		{
		}

		public ItemTemplate Template
		{
			get { return m_template; }
		}

		public LockEntry Lock
		{
			get
			{
				return m_template.Lock;
			}
		}

		public override bool IsInWorld
		{
			get { return m_isInWorld; }
		}

		/// <summary>
		/// Whether this object has already been deleted.
		/// </summary>
		public bool IsDeleted
		{
			get;
			internal set;
		}

		/// <summary>
		/// Checks whether this Item can currently be used
		/// TODO: Disarm
		/// </summary>
		public bool CanBeUsed
		{
			get { return (MaxDurability == 0 || Durability > 0) && m_loot == null; }
		}

		/// <summary>
		/// The name of this item
		/// </summary>
		public string Name
		{
			get
			{
				if (m_template != null)
				{
					return m_template.DefaultName;
				}
				return "";
			}
		}

		public override ObjectTypeId ObjectTypeId
		{
			get { return ObjectTypeId.Item; }
		}

		public bool IsContainer
		{
			get { return ObjectTypeId == ObjectTypeId.Container; }
		}

		public bool CanBeTraded
		{
			get
			{
				if (IsContainer && !((Container)this).BaseInventory.IsEmpty)
				{
					return false;
				}
				if (Owner != null && Owner is Character)
				{
					var chr = (Character)Owner;
					if (chr.IsLooting && chr.Loot.Lootable.EntityId == EntityId)
					{
						// item is currently being looted (eg a strongbox etc)
						return false;
					}
				}
				return m_template.MaxDurability == 0 || Durability > 0;
			}
		}
		/// <summary>
		/// See IUsable.Owner
		/// </summary>
		public Unit Owner
		{
			get { return m_owningCharacter; }
		}

		/// <summary>
		/// Whether this Item is currently equipped and is not a kind of container.
		/// </summary>
		public bool IsEquippedItem
		{
			get
			{
				if (m_container != null)
					return m_container == m_owningCharacter.Inventory && m_record.Slot < (int)InventorySlot.Bag1;
				else
					return false;
			}
		}

		/// <summary>
		/// Whether this Item is currently equipped.
		/// </summary>
		public bool IsEquipped
		{
			get
			{
				return m_container == m_owningCharacter.Inventory && m_record.Slot <= (int)InventorySlot.BagLast;
			}
		}

		/// <summary>
		/// Whether this is a Container and it is currently
		/// equipped or in a bankbag slot (so Items can be put into it).
		/// </summary>
		public bool IsMountedContainer
		{
			get { return m_container == m_owningCharacter.Inventory && ItemMgr.ContainerSlotsWithBank[Slot]; }
		}

		public bool IsBuyback
		{
			get
			{
				return m_record.Slot >= (int)InventorySlot.BuyBack1 &&
					   m_record.Slot <= (int)InventorySlot.BuyBackLast &&
					   m_container == m_owningCharacter.Inventory;
			}
		}

		/// <summary>
		/// Called when this Item was added to someone's inventory
		/// </summary>
		protected internal void OnAdd()
		{
			if (m_template.BondType == ItemBondType.OnPickup ||
				m_template.BondType == ItemBondType.Quest)
			{
				Flags |= ItemFlags.Soulbound;
			}

			// The container information was updated during the container's add function
			m_owningCharacter = m_container.Owner;
		}

		/// <summary>
		/// Saves all recent changes that were made to this Item to the DB
		/// </summary>
		public void Save()
		{
			if (IsDeleted)
			{
				LogUtil.ErrorException(new InvalidOperationException("Trying to save deleted Item: " + this));
				return;
			}

			m_record.SaveAndFlush();
		}

		/// <summary>
		/// Subtracts the given amount from this item and creates a new item, with that amount.
		/// WARNING: Make sure that this item is belonging to someone and that amount is valid!
		/// </summary>
		/// <param name="amount">The amount of the newly created item</param>
		public Item Split(uint amount)
		{
			Amount -= amount;
			return Create(m_template, OwningCharacter, amount);
		}

		/// <summary>
		/// Creates a new Item of this type with the given amount.
		/// Usually only used on stackable Items that do not have individual
		/// properties (like durability, enchants etc).
		/// WARNING: Make sure that this item is belonging to someone and that amount is valid!
		/// </summary>
		/// <param name="amount">The amount of the newly created item</param>
		public Item CreateNew(uint amount)
		{
			return Create(m_template, OwningCharacter, amount);
		}

		/// <summary>
		/// TODO: Random properties
		/// </summary>
		public bool CanStackWith(Item otherItem)
		{
			return m_template.IsStackable && m_template == otherItem.m_template;
		}

		public override void Dispose(bool disposing)
		{
			m_owningCharacter = null;
			m_isInWorld = false;
			IsDeleted = true;
		}

		/// <summary>
		/// A chest was looted empty
		/// </summary>
		public override void OnFinishedLooting()
		{
			Destroy();
		}

		public override uint GetLootId(LootEntryType type)
		{
			return m_template.Id;
		}

		#region Enchanting
		/// <summary>
		/// All applied Enchantments.
		/// Could return null if it doesn't have any.
		/// </summary>
		public ItemEnchantment[] Enchantments
		{
			get { return m_enchantments; }
		}

		public bool HasGems
		{
			get
			{
				if (m_enchantments != null && m_template.HasSockets)
				{
					for (var i = 0; i < ItemConstants.MaxSocketCount; i++)
					{
						if (m_enchantments[i + (int)EnchantSlot.Socket1] != null)
						{
							return true;
						}
					}
				}
				return false;
			}
		}

		public bool HasGem(ItemId id)
		{
			if (m_enchantments != null && m_template.HasSockets)
			{
				for (var i = 0; i < ItemConstants.MaxSocketCount; i++)
				{
					if (m_enchantments[i + (int)EnchantSlot.Socket1] != null &&
						m_enchantments[i + (int)EnchantSlot.Socket1].Entry.GemTemplate.ItemId == id)
					{
						return true;
					}
				}
			}
			return false;
		}

		public bool IsEnchanted
		{
			get
			{
				return m_enchantments[(int)EnchantSlot.Permanent] != null ||
					   m_enchantments[(int)EnchantSlot.Temporary] != null;
			}
		}

		private int GetEnchantSlot(EnchantSlot slot, EnchantInfoOffset offset)
		{
			return (int)ItemFields.ENCHANTMENT_1_1 + ((int)slot * 3) + (int)offset;
		}

		public void SetEnchantId(EnchantSlot slot, uint value)
		{
			var enchBase = GetEnchantSlot(slot, EnchantInfoOffset.Id);

			SetUInt32(enchBase, value);
		}

		public void SetEnchantDuration(EnchantSlot slot, int value)
		{
			var enchBase = GetEnchantSlot(slot, EnchantInfoOffset.Duration);

			SetInt32(enchBase + 1, value);
		}

		public void SetEnchantCharges(EnchantSlot slot, int value)
		{
			var enchBase = GetEnchantSlot(slot, EnchantInfoOffset.Charges);

			SetInt32(enchBase + 2, value);
		}

		/// <summary>
		/// The time until the given Enchantment expires or <see cref="TimeSpan.Zero"/> if not temporary
		/// </summary>
		/// <param name="enchantSlot"></param>
		/// <returns></returns>
		public TimeSpan GetRemainingEnchantDuration(EnchantSlot enchantSlot)
		{
			return m_enchantments[(uint)enchantSlot].RemainingTime;
		}

		//public void ApplyRandomProps(BaseItemRandomPropertyInfo info)
		//{
		//    if (info.Chance < Utility.Random(0f, 100f))
		//    {
		//        if (info is ItemRandomSuffixInfo)
		//        {
		//            ApplyRandomProps((ItemRandomSuffixInfo)info);
		//        }
		//        else
		//        {
		//            ApplyRandomProps((ItemRandomEnchantEntry)info);
		//        }
		//    }
		//}

		///// <summary>
		///// NYI
		///// </summary>
		///// <param name="info"></param>
		//public void ApplyRandomProps(ItemRandomSuffixInfo info)
		//{

		//}

		///// <summary>
		///// NYI
		///// </summary>
		///// <param name="entry"></param>
		//public void ApplyRandomProps(ItemRandomEnchantEntry entry)
		//{

		//}

		void EnsureEnchantments()
		{
			if (m_enchantments == null)
			{
				m_enchantments = new ItemEnchantment[ItemConstants.MaxEnchantsPerItem];
			}
		}

		public ItemEnchantment GetEnchantment(EnchantSlot slot)
		{
			if (m_enchantments == null)
			{
				return null;
			}
			return m_enchantments[(uint)slot];
		}

		public void ApplyEnchant(int enchantEntryId, EnchantSlot enchantSlot, int duration, int charges)
		{
			if (enchantEntryId != 0)
			{
				var enchant = EnchantMgr.GetEnchantmentEntry((uint)enchantEntryId);
				if (enchant != null)
				{
					ApplyEnchant(enchant, enchantSlot, duration, charges);
				}
			}
		}

		/// <summary>
		/// Adds a new the <see cref="ItemEnchantment"/> to the given Slot.
		/// Will remove any existing Enchantment in that slot.
		/// </summary>
		/// <param name="enchantSlot"></param>
		public void ApplyEnchant(ItemEnchantmentEntry enchantEntry, EnchantSlot enchantSlot, int duration, int charges)
		{
			// TODO: Add charges
			if (m_enchantments == null)
			{
				m_enchantments = new ItemEnchantment[ItemConstants.MaxEnchantsPerItem];
			}

			if (m_enchantments[(int)enchantSlot] != null)
			{
				RemoveEnchant(enchantSlot);
			}

			var enchant = new ItemEnchantment(enchantEntry, enchantSlot, DateTime.Now, duration);
			m_enchantments[(int)enchantSlot] = enchant;
			m_record.SetEnchant(enchantSlot, (int)enchant.Entry.Id, duration);

			SetEnchantId(enchantSlot, enchantEntry.Id);
			SetEnchantDuration(enchantSlot, duration);
			if (charges > 0)
			{
				SetEnchantCharges(enchantSlot, charges - 1);
			}

			var owner = OwningCharacter;

			if (owner != null)
			{
				owner.Inventory.ModUniqueCount(enchant.Entry.GemTemplate, 1);
				ItemHandler.SendEnchantLog(owner, (ItemId)EntryId, enchantEntry.Id);
				if (duration != 0)
				{
					ItemHandler.SendEnchantTimeUpdate(owner, this, duration);
				}
				if (IsEquippedItem)
				{
					// render on Character and apply boni
					SetEnchantEquipped(enchant);
				}
			}
		}

		/// <summary>
		/// Removes the <see cref="ItemEnchantment"/> from the given Slot.
		/// </summary>
		/// <param name="enchantSlot"></param>
		public void RemoveEnchant(EnchantSlot enchantSlot)
		{
			ItemEnchantment enchantment;

			if (m_enchantments == null || (enchantment = m_enchantments[(int)enchantSlot]) == null)
			{
				log.Error("Tried to remove Enchantment from unoccupied EnchantmentSlot {0} on Item {1}", enchantSlot, this);
				return;
			}
			RemoveEnchant(enchantment);
		}

		public void RemoveEnchant(ItemEnchantment enchant)
		{
			m_enchantments[(int)enchant.Slot] = null;
			m_record.SetEnchant(enchant.Slot, 0, 0);

			var owner = OwningCharacter;
			if (owner != null)
			{
				if (IsEquipped)
				{
					SetEnchantUnequipped(enchant);
				}
				owner.Inventory.ModUniqueCount(enchant.Entry.GemTemplate, -1);
			}
		}

		/// <summary>
		/// Applies the given gems to this Item. 
		/// Each gem will be matched to the socket of the same index.
		/// </summary>
		/// <param name="gems"></param>
		public void ApplyGems<T>(T[] gems)
			where T : class, IMountableItem
		{
			if (CheckGems(gems))
			{
				EnsureEnchantments();
				var matchingColors = true;
				for (var i = 0u; i < gems.Length; i++)
				{
					var gem = gems[i];
					if (gem != null && gem.Template.GemProperties != null)
					{
						// add new Gem at this index
						var socket = m_template.Sockets.Get(i);
						if (socket.Color != 0)
						{
							ApplyEnchant(gem.Template.GemProperties.Enchantment, EnchantSlot.Socket1 + i, 0, 0);
							if (gem is Item)
							{
								// destroy the newly applied Gem
								((Item)(IMountableItem)gem).Destroy();
							}
							matchingColors = matchingColors && (gem.Template.GemProperties.Color & socket.Color) != 0;
						}
					}
					else
					{
						// check for matching colors with already applied Enchant at this index
						var socket = m_template.Sockets.Get(i);
						matchingColors = matchingColors &&
							(socket.Color == 0 ||
							(m_enchantments[(uint)EnchantSlot.Socket1 + i] != null &&
							(m_enchantments[(uint)EnchantSlot.Socket1 + i].Entry.GemTemplate.GemProperties.Color & socket.Color) != 0));
					}
				}

				if (matchingColors)
				{
					if (GetEnchantment(EnchantSlot.Bonus) == null)
					{
						ApplyEnchant(m_template.SocketBonusEnchant, EnchantSlot.Bonus, 0, 0);
					}
				}
				else
				{
					if (GetEnchantment(EnchantSlot.Bonus) != null)
					{
						RemoveEnchant(EnchantSlot.Bonus);
					}
				}
			}
		}

		/// <summary>
		/// Checks whether all sockets have matching gems. If so, activate bonus enchant.
		/// </summary>
		internal void CheckSocketColors()
		{
			if (m_template.HasSockets && m_template.SocketBonusEnchant != null)
			{
				var matchingColors = true;
				for (var i = 0u; i < ItemConstants.MaxSocketCount; i++)
				{
					var socket = m_template.Sockets.Get(i);
					matchingColors = matchingColors &&
									 (socket.Color == 0 ||
									  (m_enchantments[(uint)EnchantSlot.Socket1 + i] != null &&
									   (m_enchantments[(uint)EnchantSlot.Socket1 + i].Entry.GemTemplate.GemProperties.Color &
										socket.Color) != 0));
				}

				if (matchingColors)
				{
					if (GetEnchantment(EnchantSlot.Bonus) == null)
					{
						// apply bonus Enchant
						ApplyEnchant(m_template.SocketBonusEnchant, EnchantSlot.Bonus, 0, 0);
					}
				}
				else
				{
					if (GetEnchantment(EnchantSlot.Bonus) != null)
					{
						// remove bonus Enchant
						RemoveEnchant(EnchantSlot.Bonus);
					}
				}
			}
		}

		/// <summary>
		/// Check whether the given gems match the color of the socket of the corresponding index within
		/// the gems-array.
		/// Check for unique count.
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="gems"></param>
		/// <returns></returns>
		private bool CheckGems<T>(T[] gems)
			where T : class, IMountableItem
		{
			for (var i = 0u; i < gems.Length; i++)
			{
				var gem = gems[i];
				if (gem != null)
				{
					var socket = m_template.Sockets.Get(i);
					if (socket.Color != SocketColor.None)
					{
						// only allow color gems in color sockets and meta gems in meta socket
						if ((socket.Color == SocketColor.Meta) != (gem.Template.GemProperties.Color == SocketColor.Meta))
						{
							return false;
						}

						if (IsEquipped)
						{
							// check uniqueness
							if (!m_owningCharacter.Inventory.CheckEquippedGems(gem.Template))
							{
								ItemHandler.SendInventoryError(m_owningCharacter, this, null, InventoryError.ITEM_MAX_COUNT_EQUIPPED_SOCKETED);
								return false;
							}
						}
					}
				}
			}
			return true;
		}

		void SetEnchantEquipped(ItemEnchantment enchant)
		{

			if (enchant.Slot == EnchantSlot.Permanent)
			{
				Owner.SetUInt16Low(
					PlayerFields.VISIBLE_ITEM_1_ENCHANTMENT + (Slot * ItemConstants.PlayerFieldVisibleItemSize), (ushort)enchant.Entry.Id);
			}
			else if (enchant.Slot == EnchantSlot.Temporary)
			{
				Owner.SetUInt16High(
						PlayerFields.VISIBLE_ITEM_1_ENCHANTMENT + (Slot * ItemConstants.PlayerFieldVisibleItemSize), (ushort)enchant.Entry.Id);
			}

			// TODO: Check for conditions
			for (var i = 0; i < enchant.Entry.Effects.Length; i++)
			{
				EnchantMgr.ApplyEffect(this, enchant.Entry.Effects[i]);
			}
		}

		void SetEnchantUnequipped(ItemEnchantment enchant)
		{
			if (enchant.Slot == EnchantSlot.Permanent)
			{
				Owner.SetUInt16Low(
					PlayerFields.VISIBLE_ITEM_1_ENCHANTMENT + (Slot * ItemConstants.PlayerFieldVisibleItemSize), 0);
			}
			else if (enchant.Slot == EnchantSlot.Temporary)
			{
				Owner.SetUInt16High(
						PlayerFields.VISIBLE_ITEM_1_ENCHANTMENT + (Slot * ItemConstants.PlayerFieldVisibleItemSize), 0);
			}

			for (var i = 0; i < enchant.Entry.Effects.Length; i++)
			{
				EnchantMgr.RemoveEffect(this, enchant.Entry.Effects[i]);
			}
		}
		#endregion

		#region Equipping
		/// <summary>
		/// Tries to equip this Item
		/// </summary>
		/// <returns></returns>
		public InventoryError Equip()
		{
			return m_owningCharacter.Inventory.TryEquip(m_container, Slot);
		}

		public bool Unequip()
		{
			var inv = m_owningCharacter.Inventory;
			var slotId = inv.FindFreeSlot(this, Amount);
			if (slotId.Slot == BaseInventory.INVALID_SLOT)
			{
				return false;
			}

			inv.SwapUnchecked(m_container, Slot, slotId.Container, slotId.Slot);
			return true;
		}

		public InventoryError CheckEquip(Character user)
		{
			var err = m_template.CheckEquip(user);
			//if (err == InventoryError.OK)
			//{
			//    if (m_enchantments != null)
			//    {
			//        for (var i = 0u; i < ItemConstants.MaxSocketCount; i++)
			//        {
			//            var enchant = m_enchantments[i + (uint)EnchantSlot.Socket1];
			//            if (enchant != null)
			//            {
			//                err = enchant.Entry.GemTemplate.CheckEquip(user);
			//                if (err != InventoryError.OK)
			//                {
			//                    return err;
			//                }
			//            }
			//        }
			//    }
			//}
			return err;
		}

		/// <summary>
		/// Called when this Item gets equipped.
		/// Requires region context.
		/// </summary>
		public void OnEquip()
		{
			var slot = (InventorySlot)Slot;
			var chr = OwningCharacter;
			if (!m_template.IsAmmo)
			{
				chr.SetVisibleItem(slot, this);
			}
			m_template.ApplyStatMods(chr);

			// binding
			if (m_template.BondType == ItemBondType.OnEquip)
			{
				Flags |= ItemFlags.Soulbound;
			}

			// spell casting
			if (chr.IsUsingSpell)
			{
				// equipping items cancels SpellCasts
				chr.SpellCast.Cancel();
			}

			// Apply resistance boni
			for (var i = 0; i < m_template.Resistances.Length; i++)
			{
				var res = m_template.Resistances[i];
				if (res > 0)
				{
					chr.ModBaseResistance((DamageSchool)i, res);
				}
			}

			// weapon handling
			if (m_template.IsWeapon)
			{
				if (slot == InventorySlot.MainHand)
				{
					m_owningCharacter.MainWeapon = this;
				}
				else if (slot == InventorySlot.OffHand)
				{
					m_owningCharacter.OffHandWeapon = this;
				}
				else if (slot == InventorySlot.ExtraWeapon)
				{
					m_owningCharacter.RangedWeapon = this;
				}
			}
			else if (slot == InventorySlot.Invalid)
			{
				// ammo
				chr.UpdateRangedDamage();
			}

			// block rating
			else if (m_template.InventorySlotType == InventorySlotType.Shield)
			{
				chr.UpdateBlockChance();
			}

			if (m_template.EquipSpell != null)
			{
				// cast spell
				chr.SpellCast.TriggerSingle(m_template.EquipSpell, chr);
			}
			if (m_template.HitSpell != null)
			{
				chr.AddProcHandler(m_hitProc = new ItemHitProcHandler(this, m_template.HitSpell));
			}

			// set boni
			if (m_template.Set != null)
			{
				var setCount = chr.Inventory.GetSetCount(m_template.Set);
				var boni = m_template.Set.Boni.Get(setCount - 1);
				if (boni != null)
				{
					chr.SpellCast.TriggerAll(boni, chr);
				}
			}

			// enchants
			if (m_enchantments != null)
			{
				foreach (var enchant in m_enchantments)
				{
					if (enchant != null)
					{
						SetEnchantEquipped(enchant);
					}
				}
			}

			m_template.NotifyEquip(this);
		}

		/// <summary>
		/// Called when this Item gets unequipped.
		/// Requires region context.
		/// </summary>
		public void OnUnEquip(InventorySlot slot)
		{
			var chr = OwningCharacter;
			if (!m_template.IsAmmo)
			{
				chr.SetVisibleItem(slot, null);
			}
			m_template.RemoveStatMods(chr);

			if (m_template.EquipSpell != null && m_template.EquipSpell.IsAura)
			{
				// remove buff				
				chr.Auras.Cancel(m_template.EquipSpell);
			}


			// resistances
			for (var i = 0; i < m_template.Resistances.Length; i++)
			{
				var res = m_template.Resistances[i];
				if (res > 0)
				{
					chr.ModBaseResistance((DamageSchool)i, -res);
				}
			}

			// damages
			if (m_template.IsWeapon)
			{
				if (slot == InventorySlot.MainHand)
				{
					m_owningCharacter.MainWeapon = null;
				}
				else if (slot == InventorySlot.OffHand)
				{
					m_owningCharacter.OffHandWeapon = null;
				}
				else if (slot == InventorySlot.ExtraWeapon)
				{
					m_owningCharacter.RangedWeapon = null;
				}
			}
			else if (slot == InventorySlot.Invalid)
			{
				// ammo
				chr.UpdateRangedDamage();
			}

			// block rating
			else if (m_template.InventorySlotType == InventorySlotType.Shield)
			{
				chr.UpdateBlockChance();
			}

			// set boni
			if (m_template.Set != null)
			{
				var setCount = chr.Inventory.GetSetCount(m_template.Set);
				var boni = m_template.Set.Boni.Get(setCount - 1);
				if (boni != null)
				{
					foreach (var bonus in boni)
					{
						var aura = chr.Auras[bonus, true];
						if (aura != null)
						{
							aura.Remove(false);
						}
					}
				}
			}

			// enchants
			if (m_enchantments != null)
			{
				foreach (var enchant in m_enchantments)
				{
					if (enchant != null)
					{
						SetEnchantUnequipped(enchant);
					}
				}
			}

			// hit proc
			if (m_hitProc != null)
			{
				m_owningCharacter.RemoveProcHandler(m_hitProc);
				m_hitProc = null;
			}

			m_template.NotifyUnequip(this);
		}
		#endregion

		/// <summary>
		/// Called whenever an item is used.
		/// Make sure to only call on Items whose Template has a UseSpell.
		/// </summary>
		internal void OnUse()
		{
			if (m_template.BondType == ItemBondType.OnUse)
			{
				Flags |= ItemFlags.Soulbound;
			}

			if (m_template.ConsumesAmount)
			{
				Amount--;
			}
			else if (m_template.UseSpell.ConsumesCharges)
			{
				// consume a charge
				var charges = GetSpellCharges(m_template.UseSpell.Index);
				if (charges == 1 && m_template.Class == ItemClass.Consumable)
				{
					Amount--;
				}
				else
				{
					SetSpellCharges(m_template.UseSpell.Index, charges - 1);
				}
			}

			if (IsInWorld)
			{
				m_template.NotifyUsed(this);
			}
		}

		#region Destroy / Remove
		/// <summary>
		/// Destroys the Item without further checks.
		/// Also destroys all contained Items if this is a Container.
		/// </summary>
		public void Destroy()
		{
			if (m_container != null && m_container.IsValidSlot(Slot))
			{
				m_container.Destroy(Slot);
			}
			else
			{
				DoDestroy();
			}
		}

		/// <summary>
		/// Called by the container to 
		/// </summary>
		protected internal virtual void DoDestroy()
		{
			var record = m_record;
			RealmServer.Instance.AddMessage(() =>
			{
				if (!record.IsNew)
				{
					record.DeleteAndFlush();
				}
			});
			m_record.OwnerId = 0;
			m_record = null;

			if (m_owningCharacter != null)
			{
				m_owningCharacter.RemoveOwnedObject(this);
				m_owningCharacter = null;
			}
			Dispose();
		}

		/// <summary>
		/// Removes this Item from its old Container (if it was added to any).
		/// After calling this method,
		/// make sure to either Dispose the item after removing (in this case you can also simply use <see cref="Destroy"/>
		/// or re-add it somewhere else.
		/// </summary>
		public void Remove(bool ownerChange)
		{
			if (m_container != null)
			{
				m_container.Remove(this, ownerChange);
			}
		}
		#endregion

		#region QuestHolder


		public QuestHolderInfo QuestHolderInfo
		{
			get { return m_template.QuestHolderInfo; }
		}

		public bool CanGiveQuestTo(Character chr)
		{
			return m_owningCharacter == chr;
		}
		#endregion

		public override string ToString()
		{
			return string.Format("{0}{1} (Templ: {2}, Id: {3})", Amount != 1 ? Amount + "x " : "", Template.DefaultName, m_template.Id, EntityId);
		}
	}
}
